ํ๋ก๋์ ์์ค์ ์๋ฐ์คํฌ๋ฆฝํธ ์ค๋ฅ ์ฒ๋ฆฌ ๋ง์คํฐํ๊ธฐ. ๊ธ๋ก๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ค๋ฅ๋ฅผ ์บก์ฒ, ๋ก๊น , ๊ด๋ฆฌํ์ฌ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํค๋ ๊ฒฌ๊ณ ํ ์์คํ ๊ตฌ์ถ๋ฒ์ ๋ฐฐ์ฐ์ธ์.
์๋ฐ์คํฌ๋ฆฝํธ ์ค๋ฅ ์ฒ๋ฆฌ: ๊ธ๋ก๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ํ๋ก๋์ ๊ธ ์ ๋ต
'console.log' ์ ๋ต์ด ํ๋ก๋์ ํ๊ฒฝ์ ์ถฉ๋ถํ์ง ์์ ์ด์
ํต์ ๋ ๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ์์๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ค๋ฅ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋จํ๊ฒ ๋๊ปด์ง ๋๊ฐ ๋ง์ต๋๋ค. `console.log(error)`๋ฅผ ๋น ๋ฅด๊ฒ ์ฐ์ด๋ณด๊ฑฐ๋ `debugger` ๊ตฌ๋ฌธ์ ์ฌ์ฉํ๋ฉด ๊ธ๋ฐฉ ํด๊ฒฐ๋์ฃ . ํ์ง๋ง ์ ํ๋ฆฌ์ผ์ด์ ์ด ํ๋ก๋์ ์ ๋ฐฐํฌ๋์ด ์ ์ธ๊ณ ์์ฒ ๋ช ์ ์ฌ์ฉ์๊ฐ ์๋ง์ ๊ธฐ๊ธฐ, ๋ธ๋ผ์ฐ์ , ๋คํธ์ํฌ ์กฐํฉ์ผ๋ก ์ ์ํ๊ฒ ๋๋ฉด, ์ด ์ ๊ทผ ๋ฐฉ์์ ์์ ํ ๋ถ์ ์ ํด์ง๋๋ค. ๊ฐ๋ฐ์ ์ฝ์์ ์ฐ๋ฆฌ๊ฐ ๋ค์ฌ๋ค๋ณผ ์ ์๋ ๋ธ๋๋ฐ์ค๊ฐ ๋ฉ๋๋ค.
ํ๋ก๋์ ์์ ์ฒ๋ฆฌ๋์ง ์์ ์ค๋ฅ๋ ๋จ์ํ ์์ ๊ฒฐํจ์ด ์๋๋ผ, ์ฌ์ฉ์ ๊ฒฝํ์ ์กฐ์ฉํ ํด์น๋ ์ฃผ๋ฒ์ ๋๋ค. ์ด๋ ๊ธฐ๋ฅ ๊ณ ์ฅ, ์ฌ์ฉ์ ๋ถ๋ง, ์ฅ๋ฐ๊ตฌ๋ ์ดํ๋ก ์ด์ด์ง๋ฉฐ, ๊ถ๊ทน์ ์ผ๋ก๋ ๋ธ๋๋ ํํ ์์๊ณผ ์์ต ์์ค์ ์ด๋ํฉ๋๋ค. ๊ฒฌ๊ณ ํ ์ค๋ฅ ๊ด๋ฆฌ ์์คํ ์ ์ฌ์น๊ฐ ์๋๋ผ, ์ ๋ฌธ์ ์ด๊ณ ๊ณ ํ์ง์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ๊ธฐ๋ณธ ๊ธฐ๋ฅ์ ๋๋ค. ์ด๋ ํ๋ ์ฌ์ฉ์๊ฐ ๋ณด๊ณ ํ ๋ฒ๊ทธ๋ฅผ ์ฌํํ๊ธฐ ์ํด ํ๋ฅ๋๋ '์ฌํ ๋์ ์๋ฐฉ๊ด'์์, ์ฌ์ฉ์ ๊ธฐ๋ฐ์ ์ฌ๊ฐํ ์ํฅ์ ๋ฏธ์น๊ธฐ ์ ์ ๋ฌธ์ ๋ฅผ ์๋ณํ๊ณ ํด๊ฒฐํ๋ '์ฌ์ ์๋ฐฉ ์์ง๋์ด'๋ก ๋น์ ์ ๋ณํ์ํต๋๋ค.
์ด ํฌ๊ด์ ์ธ ๊ฐ์ด๋๋ ๊ธฐ๋ณธ์ ์ธ ์บก์ฒ ๋ฉ์ปค๋์ฆ๋ถํฐ ์ ๊ตํ ๋ชจ๋ํฐ๋ง ๋ฐ ๊ธ๋ก๋ฒ ์ฌ์ฉ์์๊ฒ ์ ํฉํ ๋ฌธํ์ ๋ชจ๋ฒ ์ฌ๋ก์ ์ด๋ฅด๊ธฐ๊น์ง, ํ๋ก๋์ ์ ์ฆ์ ์ ์ฉ ๊ฐ๋ฅํ ์๋ฐ์คํฌ๋ฆฝํธ ์ค๋ฅ ๊ด๋ฆฌ ์ ๋ต์ ๊ตฌ์ถํ๋ ๊ณผ์ ์ ์๋ดํ ๊ฒ์ ๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ ์ค๋ฅ์ ํด๋ถ: ์ ์ ์๋ผ
์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ ์, ๋จผ์ ์ค๋ฅ๊ฐ ๋ฌด์์ธ์ง ์ดํดํด์ผ ํฉ๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ์์๋ ๋ฌด์ธ๊ฐ ์๋ชป๋๋ฉด ์ผ๋ฐ์ ์ผ๋ก `Error` ๊ฐ์ฒด๊ฐ ๋ฐ์(throw)ํฉ๋๋ค. ์ด ๊ฐ์ฒด๋ ๋๋ฒ๊น ์ ์ํ ์ ๋ณด์ ๋ณด๊ณ ์ ๋๋ค.
- name: ์ค๋ฅ์ ์ ํ (์: `TypeError`, `ReferenceError`, `SyntaxError`).
- message: ์ฌ๋์ด ์ฝ์ ์ ์๋ ์ค๋ฅ ์ค๋ช .
- stack: ์ค๋ฅ๋ก ์ด์ด์ง ํจ์ ํธ์ถ ์์๋ฅผ ๋ณด์ฌ์ฃผ๋ ์คํ ํธ๋ ์ด์ค ๋ฌธ์์ด. ์ด๋ ์ข ์ข ๋๋ฒ๊น ์ ๊ฐ์ฅ ์ค์ํ ์ ๋ณด์ ๋๋ค.
์ผ๋ฐ์ ์ธ ์ค๋ฅ ์ ํ
- SyntaxError: ์๋ฐ์คํฌ๋ฆฝํธ ์์ง์ด ์ธ์ด์ ๊ตฌ๋ฌธ์ ์๋ฐํ๋ ์ฝ๋๋ฅผ ๋ง๋ฌ์ ๋ ๋ฐ์ํฉ๋๋ค. ์ด์์ ์ผ๋ก๋ ๋ฆฐํฐ๋ ๋น๋ ๋๊ตฌ๋ฅผ ํตํด ๋ฐฐํฌ ์ ์ ์กํ์ผ ํฉ๋๋ค.
- ReferenceError: ์ ์ธ๋์ง ์์ ๋ณ์๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํ ๋ ๋ฐ์ํฉ๋๋ค.
- TypeError: ํจ์๊ฐ ์๋ ๊ฒ์ ํธ์ถํ๊ฑฐ๋ `null` ๋๋ `undefined`์ ์์ฑ์ ์ ๊ทผํ๋ ๋ฑ ๋ถ์ ์ ํ ์ ํ์ ๊ฐ์ ๋ํด ์ฐ์ฐ์ด ์ํ๋ ๋ ๋ฐ์ํฉ๋๋ค. ์ด๋ ํ๋ก๋์ ์์ ๊ฐ์ฅ ํํ ์ค๋ฅ ์ค ํ๋์ ๋๋ค.
- RangeError: ์ซ์ ๋ณ์๋ ๋งค๊ฐ๋ณ์๊ฐ ์ ํจํ ๋ฒ์๋ฅผ ๋ฒ์ด๋ฌ์ ๋ ๋ฐ์ํฉ๋๋ค.
๋๊ธฐ์ ์ค๋ฅ vs. ๋น๋๊ธฐ์ ์ค๋ฅ
์ค๋ฅ๊ฐ ๋๊ธฐ์ ์ฝ๋์ ๋น๋๊ธฐ์ ์ฝ๋์์ ์ด๋ป๊ฒ ๋์ํ๋์ง ๊ตฌ๋ณํ๋ ๊ฒ์ ๋งค์ฐ ์ค์ํฉ๋๋ค. `try...catch` ๋ธ๋ก์ `try` ๋ธ๋ก ๋ด์์ ๋๊ธฐ์ ์ผ๋ก ๋ฐ์ํ๋ ์ค๋ฅ๋ง ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. `setTimeout`, ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๋๋ ๋๋ถ๋ถ์ Promise ๊ธฐ๋ฐ ๋ก์ง๊ณผ ๊ฐ์ ๋น๋๊ธฐ ์์ ์ ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐ๋ ์์ ํ ๋นํจ์จ์ ์ ๋๋ค.
์์:
try {
setTimeout(() => {
throw new Error("This will not be caught!");
}, 100);
} catch (e) {
console.error("Caught error:", e); // ์ด ๋ผ์ธ์ ์ ๋ ์คํ๋์ง ์์ต๋๋ค
}
์ด๊ฒ์ด ๋ค์ธต์ ์ธ ์บก์ฒ ์ ๋ต์ด ํ์์ ์ธ ์ด์ ์ ๋๋ค. ๋ค์ํ ์ข ๋ฅ์ ์ค๋ฅ๋ฅผ ์ก๊ธฐ ์ํด์๋ ๋ค์ํ ๋๊ตฌ๊ฐ ํ์ํฉ๋๋ค.
ํต์ฌ ์ค๋ฅ ์บก์ฒ ๋ฉ์ปค๋์ฆ: ์ฒซ ๋ฒ์งธ ๋ฐฉ์ด์
ํฌ๊ด์ ์ธ ์์คํ ์ ๊ตฌ์ถํ๋ ค๋ฉด, ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ ๊ฑธ์ณ ์์ ๋ง ์ญํ ์ ํ๋ ์ฌ๋ฌ ๋ฆฌ์ค๋๋ฅผ ๋ฐฐํฌํด์ผ ํฉ๋๋ค.
1. `try...catch...finally`
`try...catch` ๊ตฌ๋ฌธ์ ๋๊ธฐ์ ์ฝ๋๋ฅผ ์ํ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์ค๋ฅ ์ฒ๋ฆฌ ๋ฉ์ปค๋์ฆ์ ๋๋ค. ์คํจํ ์ ์๋ ์ฝ๋๋ฅผ `try` ๋ธ๋ก์ผ๋ก ๊ฐ์ธ๊ณ , ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์คํ์ด ์ฆ์ `catch` ๋ธ๋ก์ผ๋ก ์ ํํฉ๋๋ค.
๊ฐ์ฅ ์ ํฉํ ๊ฒฝ์ฐ:
- JSON ํ์ฑ์ด๋ API ํธ์ถ๊ณผ ๊ฐ์ด ์์๋๋ ์ค๋ฅ์ ๋ํด ์ฌ์ฉ์ ์ ์ ๋ก์ง์ด๋ ์ฐ์ํ ๋์ฒด ์ฒ๋ฆฌ๋ฅผ ๊ตฌํํ๊ณ ์ถ์ ๋.
- ๋ชฉํ๊ฐ ๋ช ํํ๊ณ ๋ฌธ๋งฅ์ ๋ง๋ ์ค๋ฅ ์ฒ๋ฆฌ๋ฅผ ์ ๊ณตํ ๋.
์์:
function parseUserConfig(jsonString) {
try {
const config = JSON.parse(jsonString);
return config.userPreferences;
} catch (error) {
// ์ด๊ฒ์ ์๋ ค์ง, ์ ์ฌ์ ์ธ ์คํจ ์ง์ ์
๋๋ค.
// ๋์ฒด ์ฒ๋ฆฌ๋ฅผ ์ ๊ณตํ๊ณ ๋ฌธ์ ๋ฅผ ๋ณด๊ณ ํ ์ ์์ต๋๋ค.
console.error("Failed to parse user config:", error);
reportError(error, { context: 'UserConfigParsing' });
return { theme: 'default', language: 'en' }; // ์ฐ์ํ ๋์ฒด ์ฒ๋ฆฌ
}
}
2. `window.onerror`
์ด๊ฒ์ ์ ์ญ ์ค๋ฅ ํธ๋ค๋ฌ๋ก, ์ ํ๋ฆฌ์ผ์ด์ ์ด๋์์๋ ๋ฐ์ํ๋ ์ฒ๋ฆฌ๋์ง ์์ ๋๊ธฐ์ ์ค๋ฅ์ ๋ํ ์ง์ ํ ์์ ๋ง์ ๋๋ค. `try...catch` ๋ธ๋ก์ด ์์ ๋ ์ตํ์ ์๋จ์ผ๋ก ์๋ํฉ๋๋ค.
๋ค์ฏ ๊ฐ์ ์ธ์๋ฅผ ๋ฐ์ต๋๋ค:
- `message`: ์ค๋ฅ ๋ฉ์์ง ๋ฌธ์์ด.
- `source`: ์ค๋ฅ๊ฐ ๋ฐ์ํ ์คํฌ๋ฆฝํธ์ URL.
- `lineno`: ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ค ๋ฒํธ.
- `colno`: ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ด ๋ฒํธ.
- `error`: `Error` ๊ฐ์ฒด ์์ฒด (๊ฐ์ฅ ์ ์ฉํ ์ธ์!).
๊ตฌํ ์์:
window.onerror = function(message, source, lineno, colno, error) {
// ์ฒ๋ฆฌ๋์ง ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค!
console.log('Global handler caught an error:', error);
reportError(error);
// true๋ฅผ ๋ฐํํ๋ฉด ๋ธ๋ผ์ฐ์ ์ ๊ธฐ๋ณธ ์ค๋ฅ ์ฒ๋ฆฌ(์: ์ฝ์์ ๋ก๊น
)๋ฅผ ๋ง์ต๋๋ค.
return true;
};
์ฃผ์ ์ ํ ์ฌํญ: ๊ต์ฐจ ์ถ์ฒ ๋ฆฌ์์ค ๊ณต์ (CORS) ์ ์ฑ ๋๋ฌธ์, ๋ค๋ฅธ ๋๋ฉ์ธ(์: CDN)์์ ํธ์คํ ๋๋ ์คํฌ๋ฆฝํธ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ๋ธ๋ผ์ฐ์ ๋ ๋ณด์์์ ์ด์ ๋ก ์ธ๋ถ ์ ๋ณด๋ฅผ ์ข ์ข ๋๋ ํํ์ฌ ์ธ๋ชจ์๋ `"Script error."` ๋ฉ์์ง๋ฅผ ํ์ํฉ๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด ์คํฌ๋ฆฝํธ ํ๊ทธ์ `crossorigin="anonymous"` ์์ฑ์ ํฌํจํ๊ณ , ์คํฌ๋ฆฝํธ๋ฅผ ํธ์คํ ํ๋ ์๋ฒ๊ฐ `Access-Control-Allow-Origin` HTTP ํค๋๋ฅผ ํฌํจํ๋๋ก ํด์ผ ํฉ๋๋ค.
3. `window.onunhandledrejection`
Promise๋ ๋น๋๊ธฐ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ๊ทผ๋ณธ์ ์ผ๋ก ๋ฐ๊พธ์์ง๋ง, ์ฒ๋ฆฌ๋์ง ์์ ๊ฑฐ๋ถ(unhandled rejection)๋ผ๋ ์๋ก์ด ๊ณผ์ ๋ฅผ ์ ์ํฉ๋๋ค. Promise๊ฐ ๊ฑฐ๋ถ๋๊ณ `.catch()` ํธ๋ค๋ฌ๊ฐ ์ฐ๊ฒฐ๋์ด ์์ง ์์ผ๋ฉด, ๋ง์ ํ๊ฒฝ์์ ์ค๋ฅ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์กฐ์ฉํ ๋ฌด์๋ฉ๋๋ค. ๋ฐ๋ก ์ด ์ง์ ์์ `window.onunhandledrejection`์ด ์ค์ํด์ง๋๋ค.
์ด ์ ์ญ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ ํธ๋ค๋ฌ ์์ด Promise๊ฐ ๊ฑฐ๋ถ๋ ๋๋ง๋ค ์คํ๋ฉ๋๋ค. ์ด ๋ฆฌ์ค๋๊ฐ ๋ฐ๋ ์ด๋ฒคํธ ๊ฐ์ฒด์๋ `reason` ์์ฑ์ด ํฌํจ๋์ด ์์ผ๋ฉฐ, ์ด๋ ์ผ๋ฐ์ ์ผ๋ก ๋ฐ์ํ `Error` ๊ฐ์ฒด์ ๋๋ค.
๊ตฌํ ์์:
window.addEventListener('unhandledrejection', function(event) {
// 'reason' ์์ฑ์ ์ค๋ฅ ๊ฐ์ฒด๊ฐ ํฌํจ๋ฉ๋๋ค.
console.log('Global handler caught a promise rejection:', event.reason);
reportError(event.reason || 'Unknown promise rejection');
// ๊ธฐ๋ณธ ์ฒ๋ฆฌ(์: ์ฝ์ ๋ก๊น
)๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
event.preventDefault();
});
4. ์ค๋ฅ ๊ฒฝ๊ณ(Error Boundaries) (์ปดํฌ๋ํธ ๊ธฐ๋ฐ ํ๋ ์์ํฌ์ฉ)
React์ ๊ฐ์ ํ๋ ์์ํฌ๋ ์ค๋ฅ ๊ฒฝ๊ณ(Error Boundaries)๋ผ๋ ๊ฐ๋ ์ ๋์ ํ์ต๋๋ค. ์ด๋ ์์ ์ปดํฌ๋ํธ ํธ๋ฆฌ ์ด๋์์๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ค๋ฅ๋ฅผ ์ก์๋ด๊ณ , ํด๋น ์ค๋ฅ๋ฅผ ๋ก๊น ํ๋ฉฐ, ์ถฉ๋ํ ์ปดํฌ๋ํธ ํธ๋ฆฌ ๋์ ๋์ฒด UI๋ฅผ ํ์ํ๋ ์ปดํฌ๋ํธ์ ๋๋ค. ์ด๋ ๋จ์ผ ์ปดํฌ๋ํธ์ ์ค๋ฅ๊ฐ ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค์ด์ํค๋ ๊ฒ์ ๋ฐฉ์งํฉ๋๋ค.
๊ฐ๋จํ React ์์:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// ์ฌ๊ธฐ์ ๋ก๊น
์๋น์ค๋ก ์ค๋ฅ๋ฅผ ๋ณด๊ณ ํฉ๋๋ค
reportError(error, { componentStack: errorInfo.componentStack });
}
render() {
if (this.state.hasError) {
return ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค. ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจ ํด์ฃผ์ธ์.
;
}
return this.props.children;
}
}
๊ฒฌ๊ณ ํ ์ค๋ฅ ๊ด๋ฆฌ ์์คํ ๊ตฌ์ถํ๊ธฐ: ์บก์ฒ๋ถํฐ ํด๊ฒฐ๊น์ง
์ค๋ฅ๋ฅผ ์บก์ฒํ๋ ๊ฒ์ ์ฒซ ๋จ๊ณ์ผ ๋ฟ์ ๋๋ค. ์์ ํ ์์คํ ์ ํ๋ถํ ์ปจํ ์คํธ๋ฅผ ์์งํ๊ณ , ๋ฐ์ดํฐ๋ฅผ ์์ ์ ์ผ๋ก ์ ์กํ๋ฉฐ, ์๋น์ค๋ฅผ ์ฌ์ฉํ์ฌ ์ด ๋ชจ๋ ๊ฒ์ ์ดํดํ๋ ๊ณผ์ ์ ํฌํจํฉ๋๋ค.
1๋จ๊ณ: ์ค๋ฅ ๋ณด๊ณ ์ค์ํํ๊ธฐ
`window.onerror`, `onunhandledrejection`, ๊ทธ๋ฆฌ๊ณ ๋ค์ํ `catch` ๋ธ๋ก์ด ๋ชจ๋ ์์ฒด ๋ณด๊ณ ๋ก์ง์ ๊ตฌํํ๊ฒ ํ๋ ๋์ , ๋จ์ผํ๋ ์ค์ ํจ์๋ฅผ ๋ง๋์ธ์. ์ด๋ ์ผ๊ด์ฑ์ ๋ณด์ฅํ๊ณ ๋์ค์ ๋ ๋ง์ ์ปจํ ์คํธ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ๊ธฐ ์ฝ๊ฒ ๋ง๋ญ๋๋ค.
function reportError(error, extraContext = {}) {
// 1. ์ค๋ฅ ๊ฐ์ฒด ์ ๊ทํ
const normalizedError = {
message: error.message || '์ ์ ์๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.',
stack: error.stack || (new Error()).stack,
name: error.name || 'Error',
...extraContext
};
// 2. ๋ ๋ง์ ์ปจํ
์คํธ ์ถ๊ฐ (2๋จ๊ณ ์ฐธ์กฐ)
const payload = addGlobalContext(normalizedError);
// 3. ๋ฐ์ดํฐ ์ ์ก (3๋จ๊ณ ์ฐธ์กฐ)
sendErrorToServer(payload);
}
2๋จ๊ณ: ํ๋ถํ ์ปจํ ์คํธ ์์ง - ํด๊ฒฐ ๊ฐ๋ฅํ ๋ฒ๊ทธ์ ์ด์
์คํ ํธ๋ ์ด์ค๋ ์ค๋ฅ๊ฐ ์ด๋์ ๋ฐ์ํ๋์ง ์๋ ค์ค๋๋ค. ์ปจํ ์คํธ๋ ์ ๋ฐ์ํ๋์ง ์๋ ค์ค๋๋ค. ์ปจํ ์คํธ ์์ด๋ ์ถ์ธก์ ์์กดํ ์๋ฐ์ ์์ต๋๋ค. ์ค์ํ๋ `reportError` ํจ์๋ ๋ชจ๋ ์ค๋ฅ ๋ณด๊ณ ์์ ๊ฐ๋ฅํ ํ ๋ง์ ๊ด๋ จ ์ ๋ณด๋ฅผ ์ถ๊ฐํด์ผ ํฉ๋๋ค:
- ์ ํ๋ฆฌ์ผ์ด์ ๋ฒ์ : Git ์ปค๋ฐ SHA ๋๋ ๋ฆด๋ฆฌ์ค ๋ฒ์ ๋ฒํธ. ์ด๋ ๋ฒ๊ทธ๊ฐ ์๋ก์ด ๊ฒ์ธ์ง, ์ค๋๋ ๊ฒ์ธ์ง, ํน์ ๋ฐฐํฌ์ ์ผ๋ถ์ธ์ง ์๋ ๋ฐ ์ค์ํฉ๋๋ค.
- ์ฌ์ฉ์ ์ ๋ณด: ๊ณ ์ ํ ์ฌ์ฉ์ ID (๋ช ์์ ์ธ ๋์์ ์ ์ ํ ๋ณด์ ์์ด๋ ์ด๋ฉ์ผ์ด๋ ์ด๋ฆ๊ณผ ๊ฐ์ ๊ฐ์ธ ์๋ณ ์ ๋ณด๋ ์ ๋ ๋ณด๋ด์ง ๋ง์ธ์). ์ด๋ ์ํฅ ๋ฒ์๋ฅผ ์ดํดํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค(์: ํ ๋ช ์ ์ฌ์ฉ์๊ฐ ์ํฅ์ ๋ฐ์๋์ง, ์๋๋ฉด ๋ค์์ธ์ง).
- ํ๊ฒฝ ์ ๋ณด: ๋ธ๋ผ์ฐ์ ์ด๋ฆ ๋ฐ ๋ฒ์ , ์ด์ ์ฒด์ , ๊ธฐ๊ธฐ ์ ํ, ํ๋ฉด ํด์๋, ์ธ์ด ์ค์ .
- ๋ธ๋ ๋ํฌ๋ผ(Breadcrumbs): ์ค๋ฅ ๋ฐ์๊น์ง ์ด์ด์ง ์ฌ์ฉ์ ํ๋ ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ด๋ฒคํธ์ ์๊ฐ์ ๋ชฉ๋ก. ์: `['์ฌ์ฉ์๊ฐ #login-button ํด๋ฆญ', '/dashboard๋ก ์ด๋', '/api/widgets๋ก์ API ํธ์ถ ์คํจ', '์ค๋ฅ ๋ฐ์']`. ์ด๊ฒ์ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๋๋ฒ๊น ๋๊ตฌ ์ค ํ๋์ ๋๋ค.
- ์ ํ๋ฆฌ์ผ์ด์ ์ํ: ์ค๋ฅ ๋ฐ์ ์์ ์ ์ ํ๋ฆฌ์ผ์ด์ ์ํ์ ๋ํ ๋ฏผ๊ฐ ์ ๋ณด๊ฐ ์ ๊ฑฐ๋ ์ค๋ ์ท (์: ํ์ฌ Redux/Vuex ์คํ ์ด ์ํ ๋๋ ํ์ฑ URL).
- ๋คํธ์ํฌ ์ ๋ณด: ์ค๋ฅ๊ฐ API ํธ์ถ๊ณผ ๊ด๋ จ๋ ๊ฒฝ์ฐ ์์ฒญ URL, ๋ฉ์๋, ์ํ ์ฝ๋๋ฅผ ํฌํจํฉ๋๋ค.
3๋จ๊ณ: ์ ์ก ๊ณ์ธต - ์์ ์ ์ผ๋ก ์ค๋ฅ ๋ณด๋ด๊ธฐ
ํ๋ถํ ์ค๋ฅ ํ์ด๋ก๋๋ฅผ ํ๋ณดํ๋ค๋ฉด, ์ด๋ฅผ ๋ฐฑ์๋๋ ์๋ํํฐ ์๋น์ค๋ก ๋ณด๋ด์ผ ํฉ๋๋ค. ํ์ค `fetch` ํธ์ถ์ ๊ทธ๋ฅ ์ฌ์ฉํ ์๋ ์์ต๋๋ค. ์ฌ์ฉ์๊ฐ ํ์ด์ง๋ฅผ ๋ฒ์ด๋๋ ์๊ฐ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ๋ธ๋ผ์ฐ์ ๊ฐ ์์ฒญ์ด ์๋ฃ๋๊ธฐ ์ ์ ์ทจ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
์ด ์์ ์ ๊ฐ์ฅ ์ ํฉํ ๋๊ตฌ๋ `navigator.sendBeacon()`์ ๋๋ค.
`navigator.sendBeacon(url, data)`์ ์๋์ ๋ถ์ ๋ฐ ๋ก๊น ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ธฐ ์ํด ์ค๊ณ๋์์ต๋๋ค. ์ด๋ ํ์ด์ง๊ฐ ์ธ๋ก๋๋๊ธฐ ์ ์ ์์์ด ๋ณด์ฅ๋๋ HTTP POST ์์ฒญ์ ๋น๋๊ธฐ์ ์ผ๋ก ๋ณด๋ด๋ฉฐ, ๋ค๋ฅธ ์ค์ํ ๋คํธ์ํฌ ์์ฒญ๊ณผ ๊ฒฝ์ํ์ง ์์ต๋๋ค.
`sendErrorToServer` ํจ์ ์์:
function sendErrorToServer(payload) {
const endpoint = 'https://api.yourapp.com/errors';
const blob = new Blob([JSON.stringify(payload)], { type: 'application/json' });
if (navigator.sendBeacon) {
navigator.sendBeacon(endpoint, blob);
} else {
// ๊ตฌํ ๋ธ๋ผ์ฐ์ ๋ฅผ ์ํ ๋์ฒด ์ฒ๋ฆฌ
fetch(endpoint, {
method: 'POST',
body: blob,
keepalive: true // ํ์ด์ง ์ธ๋ก๋ ์ค ์์ฒญ์ ์ค์
}).catch(console.error);
}
}
4๋จ๊ณ: ์๋ํํฐ ๋ชจ๋ํฐ๋ง ์๋น์ค ํ์ฉํ๊ธฐ
์ด๋ฌํ ์ค๋ฅ๋ฅผ ์์ง, ์ ์ฅ, ๋ถ์ํ๊ธฐ ์ํด ์์ฒด ๋ฐฑ์๋๋ฅผ ๊ตฌ์ถํ ์๋ ์์ง๋ง, ์ด๋ ์๋นํ ์์ง๋์ด๋ง ๋ ธ๋ ฅ์ด ํ์ํฉ๋๋ค. ๋๋ถ๋ถ์ ํ์๊ฒ๋ ์ ๋ฌธ์ ์ธ ์ ์ฉ ์ค๋ฅ ๋ชจ๋ํฐ๋ง ์๋น์ค๋ฅผ ํ์ฉํ๋ ๊ฒ์ด ํจ์ฌ ํจ์จ์ ์ด๊ณ ๊ฐ๋ ฅํฉ๋๋ค. ์ด๋ฌํ ํ๋ซํผ์ ์ด ๋ฌธ์ ๋ฅผ ๋๊ท๋ชจ๋ก ํด๊ฒฐํ๊ธฐ ์ํด ํน๋ณํ ์ ์๋์์ต๋๋ค.
์ฃผ์ ์๋น์ค:
- Sentry: ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ์คํ ์์ค ๋ฐ ํธ์คํ ์ค๋ฅ ๋ชจ๋ํฐ๋ง ํ๋ซํผ ์ค ํ๋์ ๋๋ค. ์ค๋ฅ ๊ทธ๋ฃนํ, ๋ฆด๋ฆฌ์ค ์ถ์ ๋ฐ ํตํฉ์ ํ์ํฉ๋๋ค.
- LogRocket: ์ค๋ฅ ์ถ์ ๊ณผ ์ธ์ ๋ฆฌํ๋ ์ด๋ฅผ ๊ฒฐํฉํ์ฌ ์ฌ์ฉ์๊ฐ ์ค๋ฅ๋ฅผ ์ ๋ฐํ๊ธฐ ์ํด ์ ํํ ๋ฌด์์ ํ๋์ง ๋น๋์ค๋ก ๋ณผ ์ ์๊ฒ ํด์ค๋๋ค.
- Datadog Real User Monitoring: ๋ ํฐ ๋ชจ๋ํฐ๋ง ๋๊ตฌ ๋ชจ์์ ์ผ๋ถ๋ก ์ค๋ฅ ์ถ์ ์ ํฌํจํ๋ ํฌ๊ด์ ์ธ ๊ด์ธก ๊ฐ๋ฅ์ฑ ํ๋ซํผ์ ๋๋ค.
- Bugsnag: ์์ ์ฑ ์ ์์ ๋ช ํํ๊ณ ์คํ ๊ฐ๋ฅํ ์ค๋ฅ ๋ณด๊ณ ์๋ฅผ ์ ๊ณตํ๋ ๋ฐ ์ค์ ์ ๋ก๋๋ค.
์๋น์ค๋ฅผ ์ฌ์ฉํ๋ ์ด์ ?
- ์ง๋ฅ์ ์ธ ๊ทธ๋ฃนํ: ์์ฒ ๊ฐ์ ๊ฐ๋ณ ์ค๋ฅ ์ด๋ฒคํธ๋ฅผ ๋จ์ผํ๊ณ ์คํ ๊ฐ๋ฅํ ์ด์๋ก ์๋ ๊ทธ๋ฃนํํฉ๋๋ค.
- ์์ค ๋งต ์ง์: ํ๋ก๋์ ์ฝ๋๋ฅผ ๋-๋ฏธ๋ํ์ด(de-minify)ํ์ฌ ์ฝ๊ธฐ ์ฌ์ด ์คํ ํธ๋ ์ด์ค๋ฅผ ๋ณด์ฌ์ค ์ ์์ต๋๋ค. (์๋์์ ์์ธํ ์ค๋ช ).
- ์๋ฆผ ๋ฐ ํต์ง: Slack, PagerDuty, ์ด๋ฉ์ผ ๋ฑ๊ณผ ํตํฉํ์ฌ ์๋ก์ด ์ค๋ฅ, ํ๊ท(regression) ๋๋ ์ค๋ฅ์จ ๊ธ์ฆ์ ์๋ ค์ค๋๋ค.
- ๋์๋ณด๋ ๋ฐ ๋ถ์: ์ค๋ฅ ์ถ์ธ๋ฅผ ์๊ฐํํ๊ณ , ์ํฅ์ ์ดํดํ๋ฉฐ, ์์ ์ฐ์ ์์๋ฅผ ์ ํ๋ ๊ฐ๋ ฅํ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ํ๋ถํ ํตํฉ: Jira์ ๊ฐ์ ํ๋ก์ ํธ ๊ด๋ฆฌ ๋๊ตฌ์ ์ฐ๊ฒฐํ์ฌ ํฐ์ผ์ ์์ฑํ๊ณ , GitHub์ ๊ฐ์ ๋ฒ์ ๊ด๋ฆฌ ์์คํ ๊ณผ ์ฐ๊ฒฐํ์ฌ ์ค๋ฅ๋ฅผ ํน์ ์ปค๋ฐ์ ์ฐ๊ฒฐํฉ๋๋ค.
๋น๋ฐ ๋ณ๊ธฐ: ์ถ์๋ ์ฝ๋ ๋๋ฒ๊น ์ ์ํ ์์ค ๋งต
์ฑ๋ฅ์ ์ต์ ํํ๊ธฐ ์ํด, ํ๋ก๋์ ์๋ฐ์คํฌ๋ฆฝํธ๋ ๊ฑฐ์ ํญ์ ์ถ์(๋ณ์ ์ด๋ฆ ๋จ์ถ, ๊ณต๋ฐฑ ์ ๊ฑฐ)๋๊ณ ํธ๋์คํ์ผ(์: TypeScript๋ ์ต์ ESNext์์ ES5๋ก)๋ฉ๋๋ค. ์ด๋ ์๋ฆ๋ต๊ณ ์ฝ๊ธฐ ์ฌ์ด ์ฝ๋๋ฅผ ์ฝ์ ์ ์๋ ์๋ง์ธ ์ฝ๋๋ก ๋ง๋ญ๋๋ค.
์ด ์ถ์๋ ์ฝ๋์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด, ์คํ ํธ๋ ์ด์ค๋ `app.min.js:1:15432`์ ๊ฐ์ ๊ฒ์ ๊ฐ๋ฆฌํค๋ฏ๋ก ์ธ๋ชจ๊ฐ ์์ต๋๋ค.
๋ฐ๋ก ์ด ์ง์ ์์ ์์ค ๋งต์ด ๊ตฌ์ํฌ์ ์ญํ ์ ํฉ๋๋ค.
์์ค ๋งต์ ์ถ์๋ ํ๋ก๋์ ์ฝ๋์ ์๋ณธ ์์ค ์ฝ๋ ๊ฐ์ ๋งคํ์ ์์ฑํ๋ ํ์ผ(`.map`)์ ๋๋ค. Webpack, Vite, Rollup๊ณผ ๊ฐ์ ์ต์ ๋น๋ ๋๊ตฌ๋ ๋น๋ ๊ณผ์ ์์ ์ด๋ฅผ ์๋์ผ๋ก ์์ฑํ ์ ์์ต๋๋ค.
์ค๋ฅ ๋ชจ๋ํฐ๋ง ์๋น์ค๋ ์ด ์์ค ๋งต์ ์ฌ์ฉํ์ฌ ์ํธ ๊ฐ์ ํ๋ก๋์ ์คํ ํธ๋ ์ด์ค๋ฅผ ์๋ณธ ์์ค ํ์ผ์ ์ ํํ ์ค๊ณผ ์ด์ ๊ฐ๋ฆฌํค๋ ์๋ฆ๋ต๊ณ ์ฝ๊ธฐ ์ฌ์ด ์คํ ํธ๋ ์ด์ค๋ก ๋ค์ ๋ณํํ ์ ์์ต๋๋ค. ์ด๊ฒ์ ํ๋ ์ค๋ฅ ๋ชจ๋ํฐ๋ง ์์คํ ์ ๋จ์ฐ์ฝ ๊ฐ์ฅ ์ค์ํ ๊ธฐ๋ฅ์ ๋๋ค.
์์ ํ๋ฆ:
- ๋น๋ ๋๊ตฌ๊ฐ ์์ค ๋งต์ ์์ฑํ๋๋ก ๊ตฌ์ฑํฉ๋๋ค.
- ๋ฐฐํฌ ๊ณผ์ ์ค์ ์ด ์์ค ๋งต ํ์ผ์ ์ค๋ฅ ๋ชจ๋ํฐ๋ง ์๋น์ค(์: Sentry, Bugsnag)์ ์ ๋ก๋ํฉ๋๋ค.
- ์ค์: ์์ค ์ฝ๋๊ฐ ๊ณต๊ฐ๋์ด๋ ๊ด์ฐฎ์ง ์๋ค๋ฉด, `.map` ํ์ผ์ ์น ์๋ฒ์ ๊ณต๊ฐ์ ์ผ๋ก ๋ฐฐํฌํ์ง ๋ง์ญ์์ค. ๋ชจ๋ํฐ๋ง ์๋น์ค๊ฐ ๋น๊ณต๊ฐ๋ก ๋งคํ์ ์ฒ๋ฆฌํฉ๋๋ค.
์ฌ์ ์๋ฐฉ์ ์ค๋ฅ ๊ด๋ฆฌ ๋ฌธํ ๊ฐ๋ฐํ๊ธฐ
๊ธฐ์ ์ ์ ํฌ์ ์ ๋ฐ์ ๋ถ๊ณผํฉ๋๋ค. ์ง์ ์ผ๋ก ํจ๊ณผ์ ์ธ ์ ๋ต์ ์์ง๋์ด๋ง ํ ๋ด์ ๋ฌธํ์ ๋ณํ๋ฅผ ํ์๋ก ํฉ๋๋ค.
๋ถ๋ฅ ๋ฐ ์ฐ์ ์์ ์ง์
๋ชจ๋ํฐ๋ง ์๋น์ค๋ ๊ธ๋ฐฉ ์ค๋ฅ๋ก ๊ฐ๋ ์ฐฐ ๊ฒ์ ๋๋ค. ๋ชจ๋ ๊ฒ์ ๊ณ ์น ์๋ ์์ต๋๋ค. ๋ถ๋ฅ ํ๋ก์ธ์ค๋ฅผ ์๋ฆฝํ์ธ์:
- ์ํฅ: ์ผ๋ง๋ ๋ง์ ์ฌ์ฉ์๊ฐ ์ํฅ์ ๋ฐ์๋๊ฐ? ๊ฒฐ์ ๋ ํ์๊ฐ์ ๊ณผ ๊ฐ์ ์ค์ํ ๋น์ฆ๋์ค ํ๋ฆ์ ์ํฅ์ ๋ฏธ์น๋๊ฐ?
- ๋น๋: ์ด ์ค๋ฅ๊ฐ ์ผ๋ง๋ ์์ฃผ ๋ฐ์ํ๋๊ฐ?
- ์ต์ ์ฑ: ์ต๊ทผ ๋ฆด๋ฆฌ์ค์์ ์๋ก ๋ฐ์ํ ์ค๋ฅ(ํ๊ท)์ธ๊ฐ?
์ด ์ ๋ณด๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ค ๋ฒ๊ทธ๋ฅผ ๋จผ์ ์์ ํ ์ง ์ฐ์ ์์๋ฅผ ์ ํ์ธ์. ์ค์ํ ์ฌ์ฉ์ ์ฌ์ ์์ ๋ฐ์ํ๋ ์ํฅ์ด ํฌ๊ณ ๋น๋๊ฐ ๋์ ์ค๋ฅ๊ฐ ๋ชฉ๋ก์ ์ต์๋จ์ ์์ด์ผ ํฉ๋๋ค.
์ง๋ฅ์ ์ธ ์๋ฆผ ์ค์ ํ๊ธฐ
์๋ฆผ ํผ๋ก๋ฅผ ํผํ์ธ์. ๋ชจ๋ ๋จ์ผ ์ค๋ฅ์ ๋ํด Slack ์๋ฆผ์ ๋ณด๋ด์ง ๋ง์ธ์. ์๋ฆผ์ ์ ๋ต์ ์ผ๋ก ๊ตฌ์ฑํ์ธ์:
- ์ด์ ์ ๋ณธ ์ ์๋ ์๋ก์ด ์ค๋ฅ์ ๋ํด ์๋ฆผ์ ๋ณด๋ ๋๋ค.
- ์ด์ ์ ํด๊ฒฐ๋จ์ผ๋ก ํ์๋์์ง๋ง ๋ค์ ๋ํ๋ ์ค๋ฅ(ํ๊ท)์ ๋ํด ์๋ฆผ์ ๋ณด๋ ๋๋ค.
- ์๋ ค์ง ์ค๋ฅ์ ๋ฐ์๋ฅ ์ด ํ์ ํ๊ฒ ๊ธ์ฆํ์ ๋ ์๋ฆผ์ ๋ณด๋ ๋๋ค.
ํผ๋๋ฐฑ ๋ฃจํ ์์ฑํ๊ธฐ
์ค๋ฅ ๋ชจ๋ํฐ๋ง ๋๊ตฌ๋ฅผ ํ๋ก์ ํธ ๊ด๋ฆฌ ์์คํ ๊ณผ ํตํฉํ์ธ์. ์๋กญ๊ณ ์ค์ํ ์ค๋ฅ๊ฐ ์๋ณ๋๋ฉด, Jira๋ Asana์์ ์๋์ผ๋ก ํฐ์ผ์ ์์ฑํ๊ณ ๊ด๋ จ ํ์ ํ ๋นํ์ธ์. ๊ฐ๋ฐ์๊ฐ ๋ฒ๊ทธ๋ฅผ ์์ ํ๊ณ ์ฝ๋๋ฅผ ๋ณํฉํ๋ฉด, ์ปค๋ฐ์ ํฐ์ผ์ ์ฐ๊ฒฐํ์ธ์. ์ ๋ฒ์ ์ด ๋ฐฐํฌ๋๋ฉด, ๋ชจ๋ํฐ๋ง ๋๊ตฌ๋ ์ค๋ฅ๊ฐ ๋ ์ด์ ๋ฐ์ํ์ง ์์์ ์๋์ผ๋ก ๊ฐ์งํ๊ณ ํด๊ฒฐ๋จ์ผ๋ก ํ์ํด์ผ ํฉ๋๋ค.
๊ฒฐ๋ก : ์ฌํ ๋์ ์๋ฐฉ๊ด์์ ์ฌ์ ์๋ฐฉ ์ ๋ฌธ๊ฐ๋ก
ํ๋ก๋์ ์์ค์ ์๋ฐ์คํฌ๋ฆฝํธ ์ค๋ฅ ๊ด๋ฆฌ ์์คํ ์ ๋ชฉ์ ์ง๊ฐ ์๋๋ผ ์ฌ์ ์ ๋๋ค. ์ด๋ ํต์ฌ ์บก์ฒ ๋ฉ์ปค๋์ฆ์ธ `try...catch`, `window.onerror`, `window.onunhandledrejection`์ ๊ตฌํํ๊ณ , ๋ชจ๋ ๊ฒ์ ์ค์ํ๋ ๋ณด๊ณ ํจ์๋ก ๋ณด๋ด๋ ๊ฒ์์ ์์๋ฉ๋๋ค.
๊ทธ๋ฌ๋ ์ง์ ํ ํ์ ํด๋น ๋ณด๊ณ ์์ ๊น์ด ์๋ ์ปจํ ์คํธ๋ฅผ ์ถ๊ฐํ๊ณ , ์ ๋ฌธ ๋ชจ๋ํฐ๋ง ์๋น์ค๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ดํดํ๋ฉฐ, ์์ค ๋งต์ ํ์ฉํ์ฌ ๋๋ฒ๊น ์ ์ํํ ๊ฒฝํ์ผ๋ก ๋ง๋๋ ๊ฒ์์ ๋์ต๋๋ค. ์ด๋ฌํ ๊ธฐ์ ์ ๊ธฐ๋ฐ์ ์ฌ์ ์๋ฐฉ์ ๋ถ๋ฅ, ์ง๋ฅ์ ์ธ ์๋ฆผ, ๊ทธ๋ฆฌ๊ณ ์์ฑ๋ ํผ๋๋ฐฑ ๋ฃจํ์ ์ค์ ์ ๋ ํ ๋ฌธํ์ ๊ฒฐํฉํจ์ผ๋ก์จ, ์ํํธ์จ์ด ํ์ง์ ๋ํ ์ ๊ทผ ๋ฐฉ์์ ๋ณํ์ํฌ ์ ์์ต๋๋ค.
์ฌ์ฉ์๊ฐ ๋ฒ๊ทธ๋ฅผ ๋ณด๊ณ ํ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ๋ง์ธ์. ๋ฌด์์ด ๊ณ ์ฅ ๋ฌ๋์ง, ๋๊ตฌ์๊ฒ ์ํฅ์ ๋ฏธ์น๋์ง, ๊ทธ๋ฆฌ๊ณ ์ด๋ป๊ฒ ๊ณ ์ณ์ผ ํ๋์ง๋ฅผ ์๋ ค์ฃผ๋ ์์คํ ์ ๊ตฌ์ถํ๊ธฐ ์์ํ์ธ์โ์ข ์ข ์ฌ์ฉ์๊ฐ ์์์ฐจ๋ฆฌ๊ธฐ๋ ์ ์ ๋ง์ ๋๋ค. ์ด๊ฒ์ด ๋ฐ๋ก ์ฑ์ํ๊ณ , ์ฌ์ฉ์ ์ค์ฌ์ ์ด๋ฉฐ, ์ธ๊ณ์ ์ผ๋ก ๊ฒฝ์๋ ฅ ์๋ ์์ง๋์ด๋ง ์กฐ์ง์ ํน์ง์ ๋๋ค.